
from abc import ABC

class Dentry(ABC):
    pass

class Device(ABC):
    def __init__(self, name: str):
        self.__name = name

    def name(self):
        return self.__name

    def __repr__(self):
        return f"nm:{self.__name}"
        

class SuperBlock(ABC):
    def __init__(self, dev: Device|None):
        self.__dev = dev
        self.__root = None

    def dev(self) -> Device:
        return self.__dev
    def set_dev(self, dev: Device):
        self.__dev = dev

    def root(self) -> Dentry | None:
        return self.__root

    def destroy(self):
        #TODO:
        pass

    def __repr__(self):
        return f"dev:{self.__dev},root:{self.__root}"


class DentryCache(object):
    def __init__(self):
        self.__devs = {}


class Devices(object):
    def __init__(self):
        self.__data = {}
    def get_dev(self, dev_name: str) -> Device|None:
        if self.contains(dev_name):
            return self.__data[dev_name]
        return None

    def contains(self, dev: Device) -> bool:
        return dev_name in self.__data

    def add_dev(self, dev: Device) -> Device|None:
        if not self.contains(dev.name()):
            self.__data[dev.name()]
            return None
        else:
            old = self.__data[dev.name]
            self.__data[dev.name()] = dev
            return old

    def remove_dev(self, dev_name: str) -> Device|None:
        if self.contains(dev_name):
            old = self.__data[dev_name]
            del self.__data[dev_name]
            return old
        return None
        

class NameLookup(object):
    def __init__(self, dcache: DentryCache, devices: Devices):
        self.__dcache = DentryCache()
        self.__devices = Devices()

    def lookup_device(self, dev_name: str) -> Device|None:
        return self.__devices.get_dev(dev_name)

    def add_device(self, dev: Device):
        self.__devices.add_dev(dev) 

    def lookup_fname(self, file_name: str):
        pass


class SuperBlocks(object):
    def __init__(self):
        self.__list = []

    def append(self, sb: SuperBlock):
        self.__list.append(sb)

    def __len__(self):
        return len(self.__lis)

    def remove(self, sb: SuperBlock):
        pass


# parameter parse of fc(file_system_context)
class FileSystem(ABC):
    def __init__(self, name:str):
        self.__name = name
        self.__sb = []
        self.__flags = 0

    def get_name(self) -> str:
        return self.__name

    def _check_exist_sb(self, dev_name: str) -> (bool, SuperBlock|None):
        for s in self.__sb:
            if s.dev() is not None and s.dev().name() == dev_name:
                return (True, s)
        return False, None

    def _device_exist(self, dev_name: str, lookup: NameLookup) -> bool:
        return lookup.lookup_device(dev_name) is not None

    def mount(self, dev_name: str, flags: int, lookup: NameLookup) -> Dentry|None:
        #0. device is exist or not
        if self._device_exist(dev_name, lookup): # dev must not exist in cache
            return False

        dev = Device(dev_name) #TODO: dev = Device.from_path(dev_path)
        lookup.add_device(dev) 

        #1.TODO:  list all sbs check sb.dev.name == dev_name
        (exist, sb) = self._check_exist_sb(dev_name)
        if exist and sb is not None:
            sb.destroy()
            #TODO: remove the sb from list
            sb = None

        #2. alloc_sb if dev_name not exist in current sbs
        sb = SuperBlock(dev)
        #3. add_super_block
        self.__sb.append(sb)

        #TODO:4.1 setup device

        #5. fill_super
        #TODO: self.fill_super(sb, flags)

        self.__flags = flags;

        return sb.root()

    def kill_super_block(self, sb: object):
        pass
        #sb = find_super_block(dev_path)
        #self.remove_super_block(sb)
        return True

    def __repr__(self):
        return f"fs.{self.__name}"


import unittest 
class TestFileSystem(unittest.TestCase):
    def test_mount(self):
        fs_nm = "fat12"
        dev_nm = "./hello.img"
        fs = FileSystem(fs_nm)
        (exist, sb) = fs._check_exist_sb(dev_nm)
        self.assertFalse(exist)
        self.assertIsNone(sb)

        root = fs.mount(dev_nm, 0, None)
